﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NhJump.Persistence.Context;
using NHibernate;

namespace NhJump.Persistence.Batching
{
    /// <summary>
    /// DeferredValue schedules its query operation on a batcher and initiates the batch load
    /// lazily, when Value {get;} is called
    /// </summary>
    public class DeferredValue<T> : IValue<T>
    {
        private readonly IBatcher _batcher;

        private bool _valueHasBeenLoaded = false;
        private T _value;

        private readonly Func<IList, T> _resultTransformer;

        /// <summary>
        /// Schedule a query operation with batcher, and assume the first element of the 
        /// IList returned by NHibernate is our desired Value of type T
        /// </summary>
        /// <param name="batcher"></param>
        /// <param name="crit"></param>
        public DeferredValue(IBatcher batcher, ICriteria crit)
            : this(batcher, crit, dbResult => (T)dbResult[0])
        {
        }

        /// <summary>
        /// Schedule a query operation with batcher, and specify how to transform the
        /// IList returned by NHibernate into T
        /// </summary>
        /// <param name="batcher"></param>
        /// <param name="crit"></param>
        /// <param name="resultTransformer"></param>
        public DeferredValue(IBatcher batcher, ICriteria crit, Func<IList, T> resultTransformer)
        {
            _batcher = batcher;

            _batcher.Add(crit, result =>
                                   {
                                       _value = _resultTransformer(result);
                                       _valueHasBeenLoaded = true;
                                   });

            _resultTransformer = resultTransformer;
        }

        public T Value
        {
            get
            {
                if (! _valueHasBeenLoaded)
                    _batcher.ExecuteAndClear();

                return _value;
            }
        }
    }
}
